﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;

namespace remes.BLoc.CLU
{
   public static class Program
   {
      private const int ErrorCode = 100;
      private const int SuccessCode = 0;

      [System.STAThread()]
      public static int Main(string[] args)
      {
         CLUOptions options;
         string errorMessage;

         GetCommandLineOptions(args, out options, out errorMessage);

         if (errorMessage != null)
         {
            // there are errors                
            PrintLogo(options);
            Console.WriteLine(String.Format("Error: {0}", errorMessage));
            Console.WriteLine();
            PrintUsage();
            return ErrorCode;    // error
         }

         // at this point, we obtain good options.
         if (options == null)
         {
            // no option to process. Noop.
            return SuccessCode;
         }

         PrintLogo(options);

         try
         {
            var project = new BLocProject(options.ProjectPath, false);

            if (options.PreBuildActions)
            {
               options.WriteLine("Executing Baml Localizer Pre-Build actions...");
               int returnCode = project.DoPreBuildActions(GetConfiguration(project, options), StdOutCallback);
               if (returnCode != SuccessCode)
               {
                  return returnCode;
               }
            }
            else if (options.PostBuildActions)
            {
               options.WriteLine("Executing Baml Localizer Post-Build actions...");
               int returnCode = project.DoPostBuildActions(GetConfiguration(project, options), StdOutCallback);
               if (returnCode != SuccessCode)
               {
                  return returnCode;
               }
            }
            else
            {
               if (options.CheckUIDs)
               {
                  options.WriteLine("Executing Baml Localizer Check UID's...");
                  int returnCode = project.CheckUIDs(StdOutCallback);
                  if(returnCode != SuccessCode)
                  {
                     return returnCode;
                  }
               }
               if (options.UpdateUIDs)
               {
                  options.WriteLine("Executing Baml Localizer Update UID's...");
                  int returnCode = project.UpdateUIDs(StdOutCallback);
                  if (returnCode != SuccessCode)
                  {
                     return returnCode;
                  }
               }
               if (options.ParseAssembly)
               {
                  options.WriteLine("Executing Baml Localizer Parse assembly...");
                  int returnCode = project.ParseBaml(GetConfiguration(project, options), StdOutCallback);
                  if (returnCode != SuccessCode)
                  {
                     return returnCode;
                  }
               }
               if (options.GenerateAssemblies)
               {
                  options.WriteLine("Executing Baml Localizer generate satelite assemblies...");
                  int returnCode = project.GenerateSateliteAssemblies(GetConfiguration(project, options), null, StdOutCallback);
                  if (returnCode != SuccessCode)
                  {
                     return returnCode;
                  }
               }
            }
         }
         catch (Exception e)
         {
#if DEBUG
            throw e;
#else
                Console.WriteLine(e.Message);
                return ErrorCode;            
#endif
         }

         return SuccessCode;
      }

      private static BuildConfiguration GetConfiguration(BLocProject project, CLUOptions options)
      {
         var condition = String.Format("{0}|{1}", options.Configuration, options.Platform);

         foreach (var conf in project.Configurations)
         {
            if (String.Compare(condition, conf.Condition, StringComparison.OrdinalIgnoreCase) == 0)
            {
               return conf;
            }
         }

         return null;
      }

      private static void StdOutCallback(string line)
      {
         Console.WriteLine(line);
      }

      private static void GetCommandLineOptions(string[] args, out CLUOptions options, out string errorMessage)
      {
         CommandLine commandLine;
         try
         {
            // "*" means the option must have a value. no "*" means the option can't have a value 
            commandLine = new CommandLine(args,
                               new string[]{
                                            "prebuildactions",
                                            "postbuildactions",
                                            "checkuids",
                                            "updateuids",
                                            "parseassembly",
                                            "generateassemblies", 
                                            "*configuration",
                                            "*platform",
                                            "nologo",
                                            "help",
                                            "verbose"
                                        }
                                );
         }
         catch (ArgumentException e)
         {
            errorMessage = e.Message;
            options = null;
            return;
         }

         if (commandLine.NumArgs + commandLine.NumOpts < 1)
         {
            PrintLogo(null);
            PrintUsage();
            errorMessage = null;
            options = null;
            return;
         }

         options = new CLUOptions();

         options.ProjectPath = commandLine.GetNextArg();


         Option commandLineOption;

         while ((commandLineOption = commandLine.GetNextOption()) != null)
         {
            if (commandLineOption.Name == "prebuildactions")
            {
               options.PreBuildActions = true;
            }
            else if (commandLineOption.Name == "postbuildactions")
            {
               options.PostBuildActions = true;
            }
            else if (commandLineOption.Name == "checkuids")
            {
               options.CheckUIDs = true;
            }
            else if (commandLineOption.Name == "updateuids")
            {
               options.UpdateUIDs = true;
            }
            else if (commandLineOption.Name == "parseassembly")
            {
               options.ParseAssembly = true;
            }
            else if (commandLineOption.Name == "generateassemblies")
            {
               options.GenerateAssemblies = true;
            }
            else if (commandLineOption.Name == "nologo")
            {
               options.HasNoLogo = true;
            }
            else if (commandLineOption.Name == "help")
            {
               // we print usage and stop processing
               PrintUsage();
               errorMessage = null;
               options = null;
               return;
            }
            else if (commandLineOption.Name == "verbose")
            {
               options.IsVerbose = true;
            }
            // the following ones need value
            else if (commandLineOption.Name == "configuration")
            {
               options.Configuration = commandLineOption.Value;
            }
            else if (commandLineOption.Name == "platform")
            {
               options.Platform = commandLineOption.Value;
            }
            else
            {
               // something that we don't recognize
               errorMessage = String.Format("Invalid command line option '{0}'", commandLineOption.Name);
               return;
            }
         }

         // we passed all the test till here. Now check the combinations of the options
         errorMessage = options.CheckAndSetDefault();
      }

      private static void PrintLogo(CLUOptions option)
      {
         if (option == null || !option.HasNoLogo)
         {
            Console.WriteLine(String.Format(
               "Baml Localizer CommandLineUtility {0}\n" +
               "Copyright (c) remes GmbH 2010\n",
               GetAssemblyVersion()));
         }
      }

      private static void PrintUsage()
      {
         Console.WriteLine(
            "Syntax: BLocCLU [ProjectPath] [Options]\n" +
            "ProjectPath: Path to C# project file\n" +
            "Options:\n" +
            "  /prebuildactions               executes all pre-build actions\n" +
            "  /postbuildactions              executes all post-build actions\n" +
            "  /checkuids                     checks the uid's\n" +
            "  /updateuids                    updates the uid's\n" +
            "  /parseassembly                 parses the assembly with the original language\n" +
            "  /generateassemblies            generates the satelite assemblies\n" +
            "  /configuration:[configuration] configuration like 'DEBUG' or 'RELEASE'\n" +
            "  /platform:[platform]           platform like 'x86' or'x64'\n" +
            "  /nologo                        print no log\no" +
            "  /help                          shows this help\n" +
            "  /verbose                       extended output\n");
      }


      private static string GetAssemblyVersion()
      {
         Assembly currentAssembly = Assembly.GetExecutingAssembly();
         return currentAssembly.GetName().Version.ToString(4);
      }
   }

   internal sealed class CLUOptions
   {
      internal string ProjectPath;

      internal bool PreBuildActions;
      internal bool PostBuildActions;

      internal bool CheckUIDs;
      internal bool UpdateUIDs;
      internal bool ParseAssembly;
      internal bool GenerateAssemblies;

      internal string Configuration;
      internal string Platform;

      internal bool IsVerbose;
      internal bool HasNoLogo;

      /// <summary>
      /// return true if the operation succeeded.
      /// otherwise, return false
      /// </summary>
      internal string CheckAndSetDefault()
      {
         // we validate the options here and also set default
         // if we can
         if (PreBuildActions && PostBuildActions)
         {
            return "Can't set '/prebuildactions' and '/postbuildactions' options at the same time";
         }

         if ((PreBuildActions || PostBuildActions)
            && (CheckUIDs || UpdateUIDs || ParseAssembly || GenerateAssemblies))
         {
            return "Can't combine '/prebuildactions' or '/postbuildactions' options with '/checkuids', '/updateuids', '/parseassembly' or '/generateassemblies'";
         }

         if (string.IsNullOrEmpty(Configuration))
         {
            return "Configuration is required";
         }

         if (string.IsNullOrEmpty(Platform))
         {
            return "Platform is required";
         }

         // Rule #2: Must have an input 
         if (string.IsNullOrEmpty(ProjectPath))
         {
            return "ProjectPath is required";
         }
         else
         {
            if (!File.Exists(ProjectPath))
            {
               return String.Format("Project file '{0}' does not exist", ProjectPath);
            }
         }

         // if we come to this point, we are all fine, return null error message
         return null;
      }


      /// <summary>
      /// Write message line depending on IsVerbose flag
      /// </summary>
      internal void WriteLine(string message)
      {
         if (IsVerbose)
         {
            Console.WriteLine(message);
         }
      }

      /// <summary>
      /// Write the message depending on IsVerbose flag
      /// </summary>        
      internal void Write(string message)
      {
         if (IsVerbose)
         {
            Console.Write(message);
         }
      }
   }
}
